﻿using System.Drawing;

namespace FlashcardPdfCreator
{
	using iTextSharp.text.pdf;
	using iTextSharp.text;
	using System.Collections.Generic;
	using System.Linq;
	
	using System.IO;
	using System;

	public class Creator
	{
		private readonly int pageCapacity = 32;

		public MemoryStream CreatePdf(Func<IEnumerable<ICardData>> getCardData, FlashcardFonts fonts)
		{
			using (Document pdfDocument = new Document())
			{
				var stream = new MemoryStream();
				PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(pdfDocument, stream);
				writer.CloseStream = false;

				pdfDocument.SetPageSize(new Rectangle(PageSize.A4.Width, PageSize.A4.Height));
				pdfDocument.SetMargins(mmToDots(0), mmToDots(0), mmToDots(0), mmToDots(0));
								
				pdfDocument.Open();

				ICardData[] pageCardData = new ICardData[pageCapacity];
				int index = 0;

				foreach (var cardData in getCardData())
				{
					pageCardData[index] = cardData;
					index++;

					if (index == pageCapacity)
					{
						PrintFlashCardPage(pageCardData, pdfDocument, writer, CreateForeignFlashcard, FrontNextPositionProvider, fonts);
						PrintFlashCardPage(pageCardData, pdfDocument, writer, CreateNativeFlashcard, BackNextPositionProvider, fonts);
						Array.Clear(pageCardData, 0, pageCardData.Length);
						index = 0;
					}
				}

				if (index != 0)
				{
					PrintFlashCardPage(pageCardData, pdfDocument, writer, CreateForeignFlashcard, FrontNextPositionProvider, fonts);
					PrintFlashCardPage(pageCardData, pdfDocument, writer, CreateNativeFlashcard, BackNextPositionProvider, fonts);
				}

				pdfDocument.Close();
				stream.Seek(0, SeekOrigin.Begin);
				return stream;
			}
		}

		private static PointF BackNextPositionProvider(PointF point)
		{
			if (point.X == 0 && point.Y == 0)
			{
				point.X = 152.5f;
				point.Y = 287f;

				return point;
			}

			if (point.X > 10f)
			{
				point.X -= 47.5f;
				return point;
			}

			point.X = 152.5f;
			point.Y -= 34.625f;

			return point;
		}

		private static PdfPTable CreateForeignFlashcard(ICardData cardData, FlashcardFonts fonts)
		{
			PdfPCell cell;
			PdfPTable table = new PdfPTable(1);

			//Word cell
			cell = new PdfPCell(new Phrase(cardData.Word, fonts.Word));
			cell.FixedHeight = mmToDots(11);
			cell.BorderWidthBottom = 0;
			cell.BorderColor = new BaseColor(212, 212, 212);
			cell.BackgroundColor = new BaseColor(212, 212, 212);
			cell.HorizontalAlignment = Element.ALIGN_CENTER;
			cell.VerticalAlignment = Element.ALIGN_MIDDLE;
			table.AddCell(cell);

			//Pronunciation cell
			cell = new PdfPCell(new Phrase(cardData.Pronunciation, fonts.Pronunciation));
			cell.FixedHeight = mmToDots(6);
			cell.BorderWidthTop = 0;
			cell.BorderColor = new BaseColor(212, 212, 212);
			cell.BackgroundColor = BaseColor.WHITE;
			cell.Padding = 0;
			cell.HorizontalAlignment = Element.ALIGN_CENTER;
			cell.VerticalAlignment = Element.ALIGN_TOP;
			table.AddCell(cell);

			//Example cell
			cell = new PdfPCell(new Phrase(cardData.Example, fonts.Example));
			cell.FixedHeight = mmToDots(17.625f);
			cell.BackgroundColor = BaseColor.WHITE;
			cell.BorderWidthTop = 5;
			cell.Padding = 0;
			cell.PaddingLeft = mmToDots(3);
			cell.PaddingRight = mmToDots(3);
			cell.BorderColor = new BaseColor(212, 212, 212);
			cell.BorderColorTop = BaseColor.BLACK;
			cell.HorizontalAlignment = Element.ALIGN_CENTER;
			cell.VerticalAlignment = Element.ALIGN_MIDDLE;
			table.AddCell(cell);

			table.SetTotalWidth(new float[1] { mmToDots(47.5f) });
			table.WidthPercentage = 100;
			table.HorizontalAlignment = Element.ALIGN_LEFT;

			return table;
		}

		private static PdfPTable CreateNativeFlashcard(ICardData cardData, FlashcardFonts fonts)
		{
			PdfPCell cell;
			PdfPTable table = new PdfPTable(1);

			//Example translation cell
			cell = new PdfPCell(new Phrase(cardData.ExampleTranslation, fonts.Example));
			cell.FixedHeight = mmToDots(23.625f);
			cell.BackgroundColor = BaseColor.WHITE;
			cell.Padding = 0;
			cell.PaddingLeft = mmToDots(3);
			cell.PaddingRight = mmToDots(3);
			cell.BorderWidth = 0;
			cell.BorderColor = BaseColor.GRAY;
			cell.HorizontalAlignment = Element.ALIGN_CENTER;
			cell.VerticalAlignment = Element.ALIGN_MIDDLE;
			cell.Rotation = -180;
			table.AddCell(cell);

			//Meaning cell
			cell = new PdfPCell(new Phrase(cardData.Meaning, fonts.Word));
			cell.FixedHeight = mmToDots(11);
			cell.BorderWidthBottom = 0;
			cell.BorderWidthLeft = 0;
			cell.BorderWidthRight = 0;
			cell.BorderColor = BaseColor.GRAY;
			cell.BackgroundColor = BaseColor.WHITE;
			cell.PaddingTop = mmToDots(5);
			cell.HorizontalAlignment = Element.ALIGN_CENTER;
			cell.VerticalAlignment = Element.ALIGN_MIDDLE;
			cell.Rotation = -180;
			table.AddCell(cell);

			table.SetTotalWidth(new float[1] { mmToDots(47.5f) });
			table.WidthPercentage = 100;
			table.HorizontalAlignment = Element.ALIGN_LEFT;

			return table;
		}

		private static PointF FrontNextPositionProvider(PointF point)
		{
			if (point.X == 0 && point.Y == 0)
			{
				point.X = 10f;
				point.Y = 287f;

				return point;
			}

			if (point.X < 152.5f)
			{
				point.X += 47.5f;
				return point;
			}

			point.X = 10f;
			point.Y -= 34.625f;

			return point;
		}

		private static float mmToDots(float milimeters)
		{
			return (milimeters / 25.4f) * 72;
		}

		private static void PrintFlashCardPage<T>(IEnumerable<T> cards, Document doc, PdfWriter writer, Func<T, FlashcardFonts, PdfPTable> createFlashcard, Func<PointF, PointF> nextPositionProvider, FlashcardFonts fonts)
		{
			doc.NewPage();
			PointF point = new PointF(0f, 0f);
			
			foreach (var card in cards)
			{
				if (card == null)
					continue;

				point = nextPositionProvider(point);

				createFlashcard(card, fonts).WriteSelectedRows(0, -1, mmToDots(point.X), mmToDots(point.Y), writer.DirectContent);
			}
		}
	}
}